package com.cat2bug.junit.clazz;

import com.alibaba.fastjson.JSON;
import com.cat2bug.junit.service.ParameterService;
import com.cat2bug.junit.util.ParamMethodUtil;
import com.cat2bug.junit.util.UrlUtil;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.assertj.core.util.Arrays;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 将每个方法的参数已方法的形式体现
 */
public class AddArgeMethodOfTestClass extends AbstractAddMethodOfTestClass {
    ClassPool pool = ClassPool.getDefault();
    private Class<?> srcClass;
    private String proxyClassName;
    private String srcMethodName;
    private String parameterName;
    private CtClass paramType;

    private Object[] pramAnnotations;
    public AddArgeMethodOfTestClass(ITestClassFactory factory, String name, Class<?> srcClass, String proxyClassName, Method srcMethod, String parameterName, CtClass paramType, Object[] pramAnnotations) throws Exception {
        super(factory, name,null,null, paramType);
        this.srcClass=srcClass;
        this.proxyClassName=proxyClassName;
        this.srcMethodName=srcMethod.getName();
        this.parameterName=parameterName;
        this.paramType=paramType;
        this.pramAnnotations=pramAnnotations;
    }

    @Override
    public CtClass getReturnClasses() throws ClassNotFoundException, NotFoundException {
        return pool.get("java.lang.Object");
    }

//    private boolean isListPathVariable(CtClass paramType, Object[] pramAnnotations) throws ClassNotFoundException {
//        for (Object pa : pramAnnotations) {
//            Class paramCls = ParameterService.getInstance().stringToClass(paramType.getName());
//            if (pa instanceof PathVariable &&
//                    (ParameterService.getInstance().isArray(paramCls)||
//                    ParameterService.getInstance().isList(paramCls))
//                    ) { // 如果是路径参数
//                return true;
//            }
//        }
//        return false;
//    }

    @Override
    public String body(CtClass ctClass) throws Exception {
        StringBuffer sb =new StringBuffer();
        sb.append("{");
        Method testCaseMethod = ParameterService.getInstance().getTestCaseMethod(this.srcClass.getName(), this.proxyClassName, this.srcMethodName,
							this.parameterName, this.paramType.getName());
        // 如果测试用例类中没有生成参数的方法，就写随机创建参数值的方法；如果测试用例中有，就拷贝那个函数，并指定一个新的无参函数调用他，在调接口时，调用这个无参函数
        if(testCaseMethod==null) {
            String paramTypeName = this.paramType.getName();
            sb.append(String.format("java.lang.Class paramCls = com.cat2bug.junit.service.ParameterService.getInstance().stringToClass(\"%s\");",paramTypeName));
            sb.append("return com.cat2bug.junit.service.ParameterService.getInstance().createParameterRandomValue(paramCls);");
        } else { // 拷贝函数并创建无参函数的执行代码
            CtClass srcClass = pool.getCtClass(testCaseMethod.getDeclaringClass().getName()); // 获取原始类
            CtMethod srcMethod = srcClass.getDeclaredMethod(testCaseMethod.getName()); // 获取原始类方法
            MethodInfo methodInfo = srcMethod.getMethodInfo();
            CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
            if (codeAttribute != null) {
                LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
                        .getAttribute(LocalVariableAttribute.tag);
                int paramLen = srcMethod.getParameterTypes().length; // 参数数量
                Object[][] ans = srcMethod.getParameterAnnotations(); // 获取参数注解
                int pos = Modifier.isStatic(srcMethod.getModifiers()) ? 0 : 1; // 非静态的成员函数的第一个参数是this
                List<String> paramList = new ArrayList<>();
                // 遍历方法中的参数，并通过bean方法获取到
                for (int i = 0; i < paramLen; i++) {
                    String paramName = attr.variableName(i + pos); // 参数名称
                    String paramType = srcMethod.getParameterTypes()[i].getName(); // 参数类型
                    paramList.add(paramName);
                    sb.append(String.format("%s %s = webContext.getBean(%s.class);",paramType,paramName,paramType));
                }
                String methodName = "src"+name;

                // 将测试用例中创建参数值的方法添加到测试类中
                CtMethod newMethod = CtNewMethod.copy(srcMethod, methodName, ctClass, null);
                ctClass.addMethod(newMethod);

                // 执行并返回刚刚加入到测试类中的创建参数值的方法
                switch (testCaseMethod.getReturnType().getName()) {
                    case "long":
                        sb.append(String.format("return java.lang.Long.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "int":
                        sb.append(String.format("return java.lang.Integer.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "short":
                        sb.append(String.format("return java.lang.Short.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "double":
                        sb.append(String.format("return java.lang.Double.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "float":
                        sb.append(String.format("return java.lang.float.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "chat":
                        sb.append(String.format("return java.lang.Character.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    case "boolean":
                        sb.append(String.format("return java.lang.Boolean.valueOf(this.%s(%s));",methodName,paramList.stream().collect(Collectors.joining(","))));
                        break;
                    default:
                        sb.append(String.format("return (%s)this.%s(%s);",this.paramType.getName(), methodName,paramList.stream().collect(Collectors.joining(","))));
                }
            }
        }
        sb.append("}");
        return sb.toString();
    }

//    Long[] d (){
//        return com.alibaba.fastjson.JSON.parseArray(
//                "[2065250872143472003,8919426691492138138,1864436052136006969,1113112379344515631,967909584088288684]",
//                java.lang.Long.class
//        ).toArray(new java.lang.Long[1]);
//    }
    /**
     * 转换Class到CtClass类
     * @param clazzName 类型
     * @return  CtClass类
     * @throws Exception 异常
     */
    private static CtClass toCtClass(String clazzName) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        switch (clazzName) {
            case "int":
                return CtClass.intType;
            case "long":
                return CtClass.longType;
            case "short":
                return CtClass.shortType;
            case "double":
                return CtClass.doubleType;
            case "float":
                return CtClass.floatType;
            case "boolean":
                return CtClass.booleanType;
            case "char":
                return CtClass.charType;
            default:
                return cp.getCtClass(clazzName);
        }
    }
}
